package de.fret_save.log;

import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.util.Arrays;
import java.util.Calendar;

import org.apache.log4j.AppenderSkeleton;
import org.apache.log4j.Layout;
import org.apache.log4j.helpers.QuietWriter;
import org.apache.log4j.spi.ErrorCode;
import org.apache.log4j.spi.LoggingEvent;

/**
 * Log4J-Appender der den aktuellen Tag und die Zeit ans Logfile anhaengt.
 * 
 * @author ...
 */
public class TimeFileAppender extends AppenderSkeleton {
    private Calendar cal;
    private String filePraefix;
    private String fileSuffix;
    private int[] currentTime;
    private QuietWriter currentWriter;

    /**
     * Konstruktor
     */
    public TimeFileAppender() {
        this.cal = Calendar.getInstance();
    }

    /**
     * @param file
     */
    public void setFile(String file) {
        String val = file.trim();
        int extPos = val.lastIndexOf('.');

        this.filePraefix = val.substring(0, extPos);
        this.fileSuffix = val.substring(extPos);
    }

    /**
     * @see org.apache.log4j.AppenderSkeleton#activateOptions()
     */
    public void activateOptions() {
        if ((this.filePraefix == null) || (this.fileSuffix == null)) {
            throw new IllegalArgumentException("Kein Wert fuer File gesetzt");
        }
    }

    /**
     * @see org.apache.log4j.AppenderSkeleton#append(org.apache.log4j.spi.LoggingEvent
     *      )
     */
    protected synchronized void append(LoggingEvent event) {
        if (!ensureOpen()) {
            return;
        }

        this.currentWriter.write(this.layout.format(event));
        if (this.layout.ignoresThrowable()) {
            String[] ex = event.getThrowableStrRep();
            if (ex != null) {
                for (String str : ex) {
                    this.currentWriter.write(str);
                    this.currentWriter.write(Layout.LINE_SEP);
                }
            }
        }
        this.currentWriter.flush();
    }

    /**
     * @return true falls nun die Datei offen ist
     */
    protected boolean ensureOpen() {
        int[] time = new int[4];
        this.cal.setTimeInMillis(System.currentTimeMillis());
        time[0] = this.cal.get(Calendar.YEAR);
        time[1] = this.cal.get(Calendar.MONTH);
        time[2] = this.cal.get(Calendar.DAY_OF_MONTH);
        time[3] = this.cal.get(Calendar.HOUR_OF_DAY);

        if ((this.currentWriter == null)
                || (!Arrays.equals(this.currentTime, time))) {
            close();

            StringBuilder file = new StringBuilder(this.filePraefix);
            file.append(time[0]);
            append2digits(file, time[1] + 1);
            append2digits(file, time[2]);
            append2digits(file, time[3]);
            file.append(this.fileSuffix);

            this.currentTime = time;
            try {
                Writer w = new FileWriter(file.toString(), true);
                this.currentWriter = new QuietWriter(w, getErrorHandler());
            } catch (IOException ioex) {
                getErrorHandler().error("ensureOpen [" + file + "]", ioex,
                        ErrorCode.FILE_OPEN_FAILURE);
            }
        }

        return this.currentWriter != null;
    }

    /**
     * Haengt die zweistellige Zahl an den StringBuilder an. Sollte diese
     * kleiner 10 sein wird auch noch die 0 ergaenzt, damit also aus 5 eine 05
     * wird.
     * 
     * @param sb
     * @param num
     */
    private static void append2digits(StringBuilder sb, int num) {
        int zehner = num / 10;
        int einer = num % 10;
        sb.append((char) ('0' + zehner));
        sb.append((char) ('0' + einer));
    }

    /**
     * @see org.apache.log4j.Appender#close()
     */
    public synchronized void close() {
        if (this.currentWriter != null) {
            Writer w = this.currentWriter;
            this.currentWriter = null;

            try {
                w.close();
            } catch (IOException ioex) {
                getErrorHandler().error("currentWriter.close", ioex,
                        ErrorCode.CLOSE_FAILURE);
            }
        }
    }

    /**
     * @see org.apache.log4j.Appender#requiresLayout()
     */
    public boolean requiresLayout() {
        return true;
    }
}